package com.xyy.saas.payment.adpater.yop.callback;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.xyy.saas.payment.adpater.pingxx.domain.*;
import com.xyy.saas.payment.callback.core.handler.CastRequestHandler;
import com.xyy.saas.payment.common.Constants;
import com.xyy.saas.payment.common.exception.PaymentException;
import com.xyy.saas.payment.cores.enums.PayModeEnum;
import com.xyy.saas.payment.cores.enums.ResultCodeEnum;
import com.xyy.saas.payment.cores.enums.YopTradeStatusEnum;
import com.xyy.saas.payment.dao.model.RefundOrder;
import com.xyy.saas.payment.merchant.core.dto.AccountOpenNoticeDtoV2;
import com.xyy.saas.payment.service.ThirdUserInfoService;
import com.xyy.saas.payment.util.AmountCastUtil;
import com.xyy.saas.payment.util.ApolloUtil;
import com.xyy.saas.payment.util.DateUtil;
import com.xyy.saas.payment.util.JSONUtils;
import com.yeepay.yop.sdk.utils.Encodes;
import com.yeepay.yop.sdk.utils.Sm4Utils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
import java.util.TreeMap;

@Component
@Slf4j
public class CaseYopRequestHandler implements CastRequestHandler {
    @Resource
    private ThirdUserInfoService thirdUserInfoService;

    /**
     * 渠道
     */
    @Override
    public String getChannel() {
        return Constants.CHANNEL_YOP_NAME;
    }

    /**
     * 对象转换
     */
    @Override
    public Object castRequestHandler(String method, Map<String, String> request, HttpServletRequest requestMsg) throws Exception {
        switch (method) {
            case Constants.METHOD_PAY_SUCCESS_NAME:
                return castToPayDO(request);
            case Constants.METHOD_REFUND_SUCCESS:
                return castToRefundDO(request);
            case Constants.METHOD_WITHDRAW_NAME:
                return castToWithdrawalDO(request);
            case Constants.METHOD_CLEAR__NAME:
                return castToClearDO(request);
            case Constants.METHOD_OPEN_ACCOUNT_NAME:
                return caseToOpenAccountDo(request);
        }

        throw new UnsupportedOperationException("Not support yop callback method " + method);
    }

    /**
     * 支付
     */
    private PayDO castToPayDO(Map<String, String> request) throws PaymentException {
        Map<String, String> param = decrypt(request);
        PayDO payDO = new PayDO();
        payDO.setOrderNo(param.get("orderId"));
        payDO.setChannelChargeNo(param.get("uniqueOrderNo"));
        payDO.setAmount(AmountCastUtil.castYuan2Fen(param.get("orderAmount")));
        payDO.setChannel(Constants.CHANNEL_YOP_NAME + "_" + convertChannel(param.get("channel")));
        payDO.setPayMode(convertPayMode(param.get("payWay")));
        payDO.setMchId(param.get("merchantNo"));
        payDO.setResultCode("SUCCESS");
        payDO.setChannelTransactionNo(param.get("channelOrderId"));

        try {
            payDO.setTimePaid(DateUtil.parase(param.get("paySuccessDate"), DateUtil.DATEFORMATSECOND));
        }
        catch(Exception e) {
            log.error("CaseYopRequestHandler#castToPayDo cast time error request:{}", JSON.toJSONString(param));
        }

        return payDO;
    }

    /**
     * 退款
     */
    private RefundDO castToRefundDO(Map<String, String> request) throws PaymentException {
        Map<String, String> param = decrypt(request);
        RefundDO refundDO = new RefundDO();
        refundDO.setRefundNo(param.get("refundRequestId"));
        refundDO.setId(param.get("uniqueRefundNo"));
        refundDO.setSucceed(Boolean.TRUE);
        refundDO.setStatus(RefundOrder.STATUS_FINISH);

        if(YopTradeStatusEnum.FAILED.name().equals(param.get("status"))) {
            refundDO.setSucceed(Boolean.FALSE);
            refundDO.setStatus(RefundOrder.STATUS_FAIL);
        }

        try {
            refundDO.setTimeSucceed(DateUtil.parase(param.get("refundSuccessDate"),DateUtil.DATEFORMATSECOND));
        }
        catch(Exception e) {
            log.error("CaseYopRequestHandler#castToRefundDO cast time error request:{}", JSON.toJSONString(param));
        }

        return refundDO;
    }

    /**
     * 提现
     */
    private WithdrawalDO castToWithdrawalDO(Map<String, String> request) throws PaymentException {
        Map<String, String> param = decrypt(request);
        WithdrawalDO withdrawalDO = new WithdrawalDO();
        withdrawalDO.setId(param.get("orderNo"));
        withdrawalDO.setWithdrawNo(param.get("requestNo"));
        withdrawalDO.setFailureMsg(param.get("failReason"));
        withdrawalDO.setFee(NumberUtils.INTEGER_ZERO);
        withdrawalDO.setStatus(convertWithdrawStatus(param.get("status"), param.get("isReversed")));

        try {
            withdrawalDO.setTimeSucceeded(DateUtil.parase(param.get("finishTime"),DateUtil.DATEFORMATSECOND));
        }
        catch(Exception ex) {
            log.error("CaseYopRequestHandler#castToWithdrawalDO cast time error request:{}", JSON.toJSONString(param));
        }
        return withdrawalDO;
    }

    /**
     * 清算
     */
    private ClearDo castToClearDO(Map<String, String> request) throws PaymentException {
        Map<String, String> param = decrypt(request);
        ClearDo clearDo = new ClearDo();
        clearDo.setOrderAmount(param.get("orderAmount"));
        clearDo.setCsSuccessDate(param.get("csSuccessDate"));
        clearDo.setOrderId(param.get("orderId"));
        clearDo.setMerchantFee(param.get("merchantFee"));
        clearDo.setParentMerchantNo(param.get("parentMerchantNo"));
        clearDo.setUniqueOrderNo(param.get("uniqueOrderNo"));
        clearDo.setYpSettleAmount(param.get("ypSettleAmount"));
        clearDo.setMerchantNo(param.get("merchantNo"));
        clearDo.setStatus(param.get("merchantNo"));
        return clearDo;
    }

    /**
     * 开户
     */
    private AccountOpenNoticeDtoV2 caseToOpenAccountDo(Map<String, String> request) throws PaymentException {
        Map<String, String> param = decrypt(request);
        AccountOpenNoticeDtoV2 accountOpenNoticeDtoV2 = new AccountOpenNoticeDtoV2();
        accountOpenNoticeDtoV2.setAuditOpinion(param.get("auditOpinion"));
        accountOpenNoticeDtoV2.setApplicationStatus(param.get("applicationStatus"));
        accountOpenNoticeDtoV2.setApplicationNo(param.get("applicationNo"));
        accountOpenNoticeDtoV2.setProgressDescription(param.get("progressDescription"));
        accountOpenNoticeDtoV2.setAgreementSignUrl(param.get("agreementSignUrl"));
        accountOpenNoticeDtoV2.setRequestNo(param.get("requestNo"));
        accountOpenNoticeDtoV2.setMerchantNo(param.get("merchantNo"));
        accountOpenNoticeDtoV2.setAccountId(param.get("requestNo").split("_")[NumberUtils.INTEGER_ZERO]);
        return accountOpenNoticeDtoV2;
    }

    /**
     * 解密
     */
    private Map<String, String> decrypt(Map<String, String> request) throws PaymentException {
        try {
            log.info("CaseYopRequestHandler#decrypt request={}", JSONUtils.toJSON(request));
            String once = StringUtils.defaultIfEmpty(request.get("once"),null);
            String associatedData = StringUtils.defaultIfEmpty(request.get("associatedData"),null);
            String cipherText = request.get("cipherText");
            String key = ApolloUtil.getProperty("yop.sm4");
            String plainText = new String(Sm4Utils.decrypt_GCM_NoPadding(Encodes.decodeBase64(key),associatedData,once,cipherText));
            log.info("CaseYopRequestHandler#decrypt plainText={}", plainText);
            return JSONObject.parseObject(plainText, new TypeReference<TreeMap<String, String>>(){});
        }
        catch(Exception ex) {
            log.info("yop callback decrypt error, request=" + JSONUtils.toJSON(request) + ", errorMsg=" + ex.getMessage() ,ex);
            throw new PaymentException(ResultCodeEnum.SIGN_ERROR);
        }
    }

    /**
     * 渠道转换
     */
    private String convertChannel(String yopChannel) {
        if("WECHAT".equals(yopChannel)) {
            return Constants.CHANNEL_WX_NAME;
        }
        else if("ALIPAY".equals(yopChannel)) {
            return Constants.CHANNEL_ALI_NAME;
        }
        else if("UNIONPAY".equals(yopChannel)) {
            return Constants.CHANNEL_UNIONPAY_NAME;
        }

        throw new UnsupportedOperationException("Not support channel " + yopChannel);
    }

    /**
     * 支付方式转换
     */
    private String convertPayMode(String yopPayMode) {
        if("MINI_PROGRAM".equals(yopPayMode)) {
            return PayModeEnum.MINI.getMode();
        }
        else if("WECHAT_OFFIACCOUNT".equals(yopPayMode)) {
            return PayModeEnum.PUB.getMode();
        }

        throw new UnsupportedOperationException("Not support PayMode " + yopPayMode);
    }

    /**
     * 转换提现状态
     */
    private String convertWithdrawStatus(String status, String isReversed) {
        if(Boolean.TRUE.toString().equals(isReversed)) {
            return "refund";
        }
        else if(YopTradeStatusEnum.SUCCESS.name().equals(status)) {
            return "succeeded";
        }
        else if(YopTradeStatusEnum.FAIL.name().equals(status)) {
            return "failed";
        }

        throw new UnsupportedOperationException("Not support withdraw status " + status + ", isReversed " + isReversed);
    }
}
